package com.kylin.domain.oauth2.social.jwt.security;

import com.kylin.domain.oauth2.social.WechatOauthAuthenticationToken;
import com.kylin.domain.oauth2.social.jwt.JwtSecurityProperties;
import com.kylin.domain.oauth2.social.jwt.JwtTokenProvider;
import com.kylin.domain.oauth2.social.wechat.connect.WechatServiceProvider;
import com.kylin.domain.user.vo.WechatUserInfoVO;
import com.kylin.shared.enums.ResultCodeEnum;
import com.kylin.shared.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * token校验
 * @author Damon S.
 * @version v1.0.1
 * @date 2020年07月25日 12:00
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private final JwtTokenProvider jwtTokenProvider;
    private final JwtSecurityProperties jwtSecurityProperties;
    private final WechatServiceProvider wechatProvider;


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    @NotNull HttpServletResponse httpServletResponse,
                                    @NotNull FilterChain chain) throws ServletException, IOException {
        String authToken = httpServletRequest.getHeader(jwtSecurityProperties.getHeader());
        httpServletResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
        if (StringUtils.isEmpty(authToken) || !authToken.startsWith(jwtSecurityProperties.getStartWith())) {
            chain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        if (Objects.nonNull(SecurityContextHolder.getContext().getAuthentication())) {
            throw new BusinessException(ResultCodeEnum.AUTH_FAIL);
        }
        // 解析token，获取用户认证主体
        authToken = authToken.substring(jwtSecurityProperties.getStartWith().length());
        if (StringUtils.isEmpty(authToken)) {
            httpServletResponse.getWriter().write("");
            return;
        }
        // token验证通过
        String openId = jwtTokenProvider.getUsernameFromToken(authToken);
        WechatUserInfoVO userInfo = wechatProvider.findUserInfoByOpenId(openId);
        // 用户信息验证不合法，返回错误信息
        boolean validateToken = jwtTokenProvider.validateToken(authToken, userInfo.getOpenId());
        if (!validateToken) {
            throw new BusinessException(ResultCodeEnum.AUTH_FAIL);
        }
        // 正常验证通过
        WechatOauthAuthenticationToken authentication = new WechatOauthAuthenticationToken(userInfo);
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
        SecurityContextHolder.getContext().setAuthentication(authentication);

        logger.info(String.format("Authenticated user openId [%s], setting security context succeed.", openId));
        chain.doFilter(httpServletRequest, httpServletResponse);
    }
}
